package com.kzd.btgame.manage

import com.blankj.utilcode.util.*
import com.kzd.btgame.app.AppFlowBus
import com.kzd.btgame.db.GameDownload
import com.kzd.btgame.app.GTApplication
import com.kzd.btgame.db.Download
import com.kzd.btgame.db.GameDownloadDao
import com.module.lemlin.http.msg
import com.module.lemlin.app.OwnerApplication
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import rxhttp.*
import java.io.File
import java.lang.StringBuilder
import java.util.concurrent.atomic.AtomicReference

class DownloadManager {

    companion object {

        private val mDao: GameDownloadDao by lazy {
            GTApplication.appDatabase.gameDownloadDao()
        }

        private val mDirectoryApk: File
            get() = getDiskCacheDirectoryGame(OwnerApplication.application)

        //存放下载请求对象
        private val mDisposable: MutableMap<String, Disposable> = hashMapOf()

        private val INSTANCE = AtomicReference<DownloadManager?>()

        val instance: DownloadManager
            get() {
                while (true) {
                    var current = INSTANCE.get()
                    if (current != null) {
                        return current
                    }
                    current = DownloadManager()
                    if (INSTANCE.compareAndSet(null, current)) {
                        return current
                    }
                }
            }
    }

    fun initData() {
        mDao.loadAll()?.filter {
            it.downStatus == 0 && mDisposable[it.gameId] == null
        }?.forEach {
            val download = it?.apply {
                this.downStatus = 1
            } ?: return
            mDao.update(download)
        }
        update()
    }

    fun update() {

        val downloads = mDao.loadAll()

        val fileNames = mDirectoryApk.listFiles()?.map {
            it.name
        }?.toMutableList()

        downloads?.forEach {
            if (it == null)
                return
            val fileName = fileNames?.find { name ->
                it.fileName == name
            }
            if (fileName == null) {
                mDao.delete(it.gameId)
                val download = Download(
                    Download.Action.ACTION_DELETE,
                    GameDownload().apply { this.gameId = it.gameId }
                )
                AppFlowBus.post(Download::class.java, download)
            }
        }

        fileNames?.forEach { name ->
            val download = downloads?.find {
                name == it.fileName
            }
            if (download == null) {
                deleteFile(localPath = "${mDirectoryApk.path}/${name}")
            }
        }

        //检测下载的apk是否已安装，则将状态改为可运行打开
        downloads.filter {
            it.downStatus == 2
        }.filter {
            val packageName = AppUtils.getApkInfo(it.localPath)
                ?.packageName
            AppUtils.isAppInstalled(packageName)
        }.forEach {
            val download = it?.apply {
                this.downStatus = 4
            } ?: return
            mDao.update(download)
            AppFlowBus.post(Download::class.java,
                Download(action = Download.Action.ACTION_UPDATE, gameDownload = download))

        }

        //检测下载的apk是否已被卸载，则将状态改为可安装
        downloads.filter {
            it.downStatus == 4
        }.filter {
            val packageName = AppUtils.getApkInfo(it.localPath)
                ?.packageName
            !AppUtils.isAppInstalled(packageName)
        }.forEach {
            val download = it?.apply {
                this.downStatus = 2
            } ?: return
            mDao.update(download)
            AppFlowBus.post(Download::class.java,
                Download(action = Download.Action.ACTION_UPDATE, gameDownload = download))
        }
    }

    fun test() {
        mDao.loadAll().forEach {
            val packageName = AppUtils.getApkInfo(it.localPath)
                ?.packageName
            LogUtils.e("===${packageName}===${AppUtils.isAppInstalled(packageName)}")
        }
    }

    fun download(gameId: String, gameName: String, gameIcon: String, downPath: String) {

        if (mDisposable.size == 3) {
            ToastUtils.showShort("最多同时支持下载3款游戏，请等待下载完成！")
            return
        }
        if (mDisposable[gameId] != null)
            return
        if (!RegexUtils.isURL(downPath)) {
            ToastUtils.showShort("下载地址错误！")
            return
        }
        val fileName = try {
            val name = downPath.substring(
                downPath.lastIndexOf("/") + 1, downPath.length
            )
            val suffix = downPath.substring(
                downPath.lastIndexOf("."), downPath.length
            )
            EncryptUtils.encryptMD5ToString(
                StringBuilder().append(gameId).append(name).toString()
            ) + suffix
        } catch (e: Exception) {
            ToastUtils.showShort("下载地址错误！")
            return
        }

        val localPath = StringBuilder(mDirectoryApk.path)
            .append("/").append(fileName).toString()

        val gameDownload = mDao.selectGameDownloadByGameID(gameId)
        if (gameDownload == null) {
            val download = GameDownload().apply {
                this.gameId = gameId
                this.gameName = gameName
                this.gameIcon = gameIcon
                this.fileName = fileName
                this.downPath = downPath
                this.localPath = localPath
                this.fileSize = 0L
                this.currentSize = 0L
                this.progress = 0
                this.downStatus = -1
            }
            mDao.insert(download)
        }
        if (gameDownload?.downStatus == 2)
            return
        RxHttp.get(downPath).asAppendDownload(localPath, AndroidSchedulers.mainThread()) {
            val download = mDao.selectGameDownloadByGameID(gameId).apply {
                this.fileSize = it.totalSize
                this.currentSize = it.currentSize
                this.progress = it.progress
                this.downStatus = 0
            }
            mDao.update(download)
            AppFlowBus.post(Download::class.java,
                Download(action = Download.Action.ACTION_UPDATE, gameDownload = download))
        }.doOnSubscribe {
            val download = mDao.selectGameDownloadByGameID(gameId).apply {
                this.downStatus = -1
            }
            mDao.update(download)
            AppFlowBus.post(Download::class.java,
                Download(action = Download.Action.ACTION_UPDATE, gameDownload = download))
            mDisposable[gameId] = it
        }.doOnNext {
            val packageName = AppUtils.getApkInfo(it)
                ?.packageName
            val download = mDao.selectGameDownloadByGameID(gameId).apply {
                this.downStatus = if (AppUtils.isAppInstalled(packageName)) 4 else 2
            }
            mDao.update(download)
            AppFlowBus.post(Download::class.java,
                Download(action = Download.Action.ACTION_UPDATE, gameDownload = download))
        }.doOnError {
            ToastUtils.showShort(it.msg)

            dispose(gameId)

            val download = mDao.selectGameDownloadByGameID(gameId).apply {
                this.downStatus = 3
            }
            mDao.update(download)
            AppFlowBus.post(Download::class.java,
                Download(action = Download.Action.ACTION_UPDATE, gameDownload = download))
        }.doOnComplete {
            dispose(gameId)
        }.subscribe()
    }

    /**
     * 暂停下载
     *
     * @param gameId 下载地址
     * */
    fun stopDownload(gameId: String) {
        dispose(gameId)
        val gameDownload = mDao.selectGameDownloadByGameID(gameId).apply {
            this.downStatus = 1
        }
        mDao.update(gameDownload)
        AppFlowBus.post(Download::class.java,
            Download(action = Download.Action.ACTION_UPDATE, gameDownload = gameDownload))
    }

    private fun dispose(gameId: String) {
        mDisposable[gameId]?.dispose()
        mDisposable.remove(gameId)
    }

    fun dispose() {
        for (item in mDisposable) {
            dispose(item.key)
        }
        initData()
    }

    fun deleteDownload(gameId: String) {
        dispose(gameId)
        mDao.delete(gameId)
        val gameDownload = Download(
            Download.Action.ACTION_DELETE,
            GameDownload().apply { this.gameId = gameId }
        )
        AppFlowBus.post(Download::class.java,gameDownload)
    }

    fun deleteDownload(gameIds: MutableList<String>) {
        for (item in gameIds) {
            deleteDownload(item)
        }
    }

    fun deleteFile(localPath: String) = try {
        val file = File(localPath)
        if (!file.exists())
            throw Exception()
        file.delete()
    } catch (e: Exception) {
        e.printStackTrace()
    }

    fun deleteFile(localPaths: MutableList<String>) {
        for (item in localPaths) {
            deleteFile(item)
        }
    }

}